package neuralmusic.brain;

import java.awt.Rectangle;
import java.util.ArrayList;
import java.util.List;
import java.util.Random;

import uk.co.drpj.util.tweaks.TweakableDouble;

import neuralmusic.brain.module.Brain;
import neuralmusic.brain.module.Connection;
import neuralmusic.brain.module.Module;
import neuralmusic.brain.module.RandomExcitor;
import neuralmusic.brain.module.Scheduler;
import neuralmusic.brain.module.GrayModule;
import neuralmusic.brain.module.Layer;
import neuralmusic.brain.module.Neuron;
import neuralmusic.brain.module.NeuronParams;
import neuralmusic.brain.module.Point;

/***
 * Builds a layer with neurons that are used as both input and out put.
 * 
 */
public class MirrorIOBrainBuilder {

	
	/***
	 * 
	 * @param nIO     number of IO connections 
	 * @param nIn     connections from layer into brain
	 * @param nOut    connections from brain to IO layer
	 * @param nInternal
	 * @param nInternalConnect
	 * @param nInToGrey
	 * @param nGreyToOut
	 * @param pramIn
	 * @param pramGrey
	 * @param pramOut
	 * @param inputConnectWeight
	 * @param weightInToGrey
	 * @param internalConnectWeight
	 * @param weightGrayToOut
	 * @param delayMin
	 * @param delayMax
	 * @param rect
	 * @param rand
	 * @return
	 */
	public static Brain buildBrain(int nIO,int nIn, int nOut, int nInternal,
			int nInternalConnect, int nInToGrey, int nGreyToOut,
			NeuronParams pramIn, NeuronParams pramGrey, NeuronParams pramOut,
			Float inputConnectWeight, Float weightInToGrey,
			Float internalConnectWeight, Float weightGrayToOut, float delayMin,
			float delayMax, Rectangle rect,Random rand) {

	//	Scheduler fireQueue = sched; //new RTScheduler();
		Brain brain = new Brain(rand);

		int marg = 20;

		Rectangle rect2 = new Rectangle(rect.x + marg, rect.y + marg,
				rect.width - 3 * marg, rect.height - 2 * marg);

		double distFract=.3;
		GrayModule gray = new GrayModule(nInternal, nInternalConnect, rect2,
				-internalConnectWeight, internalConnectWeight, pramGrey, brain,
				delayMin, delayMax, brain.tweaks,distFract);
		brain.addModule(gray);

		// INPUTS

		Point p1 = new Point(rect.x, rect.y);
		Point p2 = new Point(rect.x, rect.height);

		Layer ioLayer = new Layer("INOUT", nIO, p1, p2, pramIn, brain,
				brain.tweaks);
		
		
		brain.addModule(ioLayer);

		ioLayer.createInputConnections(inputConnectWeight, brain);

		brain.inputs = ioLayer.inputs;

		ioLayer.createOutputConnections(1.0f, brain);
		brain.outputs = ioLayer.outputs;

		connectInputsToGreyMatter(ioLayer, gray, nInToGrey, weightInToGrey,
				brain,rand);
		

		connectGreyMatterToOutputs(ioLayer, gray, nGreyToOut, weightGrayToOut,
				brain,rand);
	
		
		return brain;
	}

	// connect the input neurons to the grey stuff
	
	public static void connectInputsToGreyMatter(Layer inLayer,
			GrayModule gray, int nConnect, float weightMax,
			Brain brain,Random rand ){

	
		int size = gray.neurons.size();

		List<Connection> newConnections = new ArrayList<Connection>();

		for (Neuron n : inLayer.neurons) {
			for (int i = 0; i < nConnect; i++) {
				Neuron out = gray.neurons.get((int) (rand.nextDouble() * size));
				float weight = (rand.nextFloat() - 0.5f) * 2.0f * weightMax;
				
				Connection c2 = brain.createConnection(n, out, weight);
				newConnections.add(c2);

			}
		}

		inLayer.outputs = newConnections;
		gray.inputs = newConnections;
	}

	// connect the input neurons to the grey stuff
	public static void connectGreyMatterToOutputs(Layer outLayer,
			GrayModule gray, int nConnect, float weightMax,
			Brain brain,Random rand) {

		List<Connection> newConnections = new ArrayList<Connection>();
	
		
		int size = gray.neurons.size();

		for (Neuron o : outLayer.neurons) {
			for (int i = 0; i < nConnect; i++) {
				Neuron grayN = gray.neurons
						.get((int) (rand.nextDouble() * size));

				float weight = (rand.nextFloat() - 0.5f) * 2.0f * weightMax;
				Connection c = brain.createConnection(grayN,o, weight);
				newConnections.add(c);
			}
		}

		outLayer.inputs = newConnections;
		gray.outputs = newConnections;
	}

}
